/*
*   Queue.c
*   Authors: Dustin Striplin, Bill Minter, Wilson Burhan
*   Holds objects of type PCBPtr, implemented as a singley linked list.
*/

#include <stdlib.h>
#include <stdio.h>
#include "Queue.h"
//#include "ProcessADT.c"
//#include "pcb.c"

NodePtr nodeConstructor(PCBPtr contents) {
    NodePtr toReturn = (NodePtr) malloc(sizeof(NodeStr));
    toReturn->contents = contents;
    toReturn->next = (NodePtr)NULL;
    return toReturn;
}

//allocate memory on the heap
QueuePtr queueConstructor() {
    QueuePtr toReturn = (QueuePtr) malloc(sizeof(QueueStr));
    toReturn->head = (NodePtr)NULL;
    toReturn->tail = (NodePtr)NULL;

    //function pointers
    toReturn->destruct = queueDestructor;
    toReturn->destructAll = queueDestructAll;
    toReturn->isEmpty = queueIsEmpty;
    toReturn->enqueue = queueEnqueue;
    toReturn->dequeue = queueDequeue;
    toReturn->peek = queuePeek;

    return toReturn;
}

//destroy all of the nodes & the queue data structure, leave processes and pcbs alone.
void queueDestructor(QueuePtr this) {
    while(!this->isEmpty(this)) {
        this->dequeue(this);
    }
    free(this);  //destroy the queue
}

/*
*   Destroys all pcbs, processes, requests, nodes, and the queue
*/
void queueDestructAll(QueuePtr this) {
    PCBPtr element = NULL;
    while(!this->isEmpty(this)) {
        element = this->dequeue(this);              //this destroys the node
        if(element != (PCBPtr) NULL) {
            if(element->process != (ProcessPtr) NULL) {
                RequestPtr* requests = element->process->requests;
                int i = 0;
                for(;i < element->process->no_requests; i++) {  //destroy all requests
                    requests[i]->destruct(requests[i]);
                }
                free(requests);                                 //destroy request array
                element->process->destruct(element->process);  //destroy the process
            }
           element->destruct(element);                         //destroy the PCB
        }
    }
    free(this);       //destroy the queue
}

//add the pcb to the queue
int queueEnqueue(QueuePtr this, PCBPtr pcb) {
    NodePtr newNode = nodeConstructor(pcb);

    if(this->isEmpty(this)) {
        this->head = newNode;
        this->tail = newNode;
    } else {
        this->tail->next = newNode; //move newNode to the end of the queue
        this->tail = newNode;
    }
}

/*remove the last node & return the pcbstr that it contains
*   returns null if the queue is empty
*   otherwise FIFO
*/
PCBPtr queueDequeue(QueuePtr this) {
    if(this->isEmpty(this))
        return NULL;
    PCBPtr toReturn = this->head->contents;
    NodePtr tmp = this->head;
    this->head = this->head->next;
    free(tmp);  //deallocate
    return toReturn;
}


//is it empty?
bool queueIsEmpty(QueuePtr this)    {

    if(this->head == (NodePtr)NULL) {
        return TRUE;
    }
    return FALSE;
}

//peek
PCBPtr queuePeek(QueuePtr this) {
    if(this->isEmpty(this))
        return NULL;
    return this->head->contents;
}

/*
int main() {
    printf("hello from queueeueueue\n");
    QueuePtr testQueue = queueConstructor();

    //create a pcb associated with the process
    int queueSize = 10;
    int no_requests = 10;
    int i = 1;
    for(i; i <= queueSize; i++) {
        RequestPtr* requests = (RequestPtr*) malloc(sizeof(RequestPtr)*no_requests);
        int j = 0;
        for(;j<no_requests;j++) {
            requests[j] = requestConstructor(i,j);
        }
        ProcessPtr proc = processConstructor(NOTHING, 10, no_requests, requests);
        testQueue->enqueue(testQueue, pcbConstructor(i, proc));
    }

    while(!testQueue->isEmpty(testQueue)) {
        printf("Node: %d dequeued\n", ((PCBPtr)testQueue->dequeue(testQueue))->pid);
    }
    */
    //DO IT AGAIN
    /*
    i = 1;
    for(i; i <= queueSize; i++) {
        RequestPtr* requests = (RequestPtr*) malloc(sizeof(RequestPtr)*no_requests);
        int j = 0;
        for(;j<no_requests;j++) {
            requests[j] = requestConstructor(i,j);
        }
        ProcessPtr proc = processConstructor(NOTHING, 10, no_requests, requests);
        testQueue->enqueue(testQueue, pcbConstructor(i, proc));
    }

    while(!testQueue->isEmpty(testQueue)) {
        printf("Node: %d dequeued\n", ((PCBPtr)testQueue->dequeue(testQueue))->pid);
    }
    */
/*
    testQueue->destructAll(testQueue);
}

*/